import json
from datetime import datetime

import requests
from langchain_community.chat_models.tongyi import ChatTongyi


class EnhancedDBQA:
    def __init__(self, student_id: str):
        self.llm = ChatTongyi(
            api_key="sk-034e603cf4a74380a674a9e7f5856648",
            model="qwen-plus"
        )

        self.aggregated_data = {}

        # 新增接口路由配置
        self.service_endpoints = {
            "basic_info": {
                "url": "http://localhost:9002/student/findByID",
                "method": "POST",
                "params": {"studentId": student_id},
                "description": "学生个人基本信息"
            },
            "exam_scores": {
                "url": "http://localhost:9002//course/getScoreByInfo",
                "method": "POST",
                "params": {"studentId": student_id, "coursename": "","grade":"","teacher":""},
                "description": "考试成绩查询"
            },
            "free_room":{
                "url": "http://localhost:9002/room/findRoom",
                "method": "POST",
                "params": {"aircon":"", "building":"", "computer":"", "scale":"", "weekday":"", "startTime":"", "endTime":""},
                "description":"空闲教室查询"
            },
            "get_purchase": {
                "url": "http://localhost:9002/card/getPurcByDate",
                "method": "POST",
                "params": {"studentID": student_id, "startDate":"1970-01-01", "endDate":"2099-12-31"},
                "description": "校园卡消费流水"
            },
            "get_AllScore":{
                "url": "http://localhost:9002/course/getScoreByID",
                "method": "POST",
                "params": {"studentId": student_id},
                "description":"学生所有考试成绩"
            },
            "get_chosenCourse":{
                "url": "http://localhost:9002/course/getChosenCourse",
                "method": "POST",
                "params": {"studentId": student_id},
                "description":"学生本学期的所有课程"
            },
            "get_AllCourse":{
                "url": "http://localhost:9002/course/getAllcourse",
                "method": "POST",
                "params": {},
                "description":"已开放可选的所有课程"
            }
        }

        self.logistical_services = [
            "basic_info",
            "get_AllScore",
            "get_chosenCourse",
            "get_AllCourse",
            "get_purchase"
        ]

    #工具类，生成可读的所有接口的自然语言描述
    def _list_available_services(self) -> str:
        return "\n".join(
            [f"{k}: {v['description']}（支持参数：{', '.join(v['params'].keys())}）"
             for k, v in self.service_endpoints.items()]
        )

    # 获得原始问题输入，解析用户的意图返回
    def parse_intent(self, question: str) -> dict:
        now_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        prompt = f"""请严格按以下格式返回JSON：
           {{
               "intent": "服务标识符", 
               "parameters": {{"参数名": "参数值"}}
           }}

           可用服务：
           {self._list_available_services()}

           问题：{question}

           注意：
           1. 必须使用双引号
           2. 不要包含任何额外文本
           3. 参数值保持原始值，不要解释
           4. 如果读取到了表示时间的自然语言（除了校园卡流水查询部分），将其作为json返回的时候需要转换成对应的int类型的数字，例如：
           读到周一第二到第三节课，需要将weekday设置为1，startTime设置为2，endTime设置为3
           5.如果是与校园卡流水查询有关的时间，需要将读到的时间格式设置成yyyy-mm-dd的格式
           6.如果涉及到空闲教室查询相关功能，且带有教学楼的描述，需要在设置参数时将building字段设置成A/B/C中与用户描述最接近的
           7.如果涉及到空闲教室查询相关功能，且带有教室设施的相关的描述，需要在设置参数时将表示多媒体的computer和表示空调的aircon字段设置成与用户描述最接近的，有空调或多媒体的教室，对应的字段值为1
           8.现在的时间是{now_time}
           9.用户的原始问题但凡涉及到带有”推荐课程，成绩分析“等需要进行推理的过程时，将intent设置为”logistical“
           10.用户的原始问题涉及到带有”不及格，及格，以上，以下“等带有除了时间之外的范围性描述时，将intent设置为”logistical
           """

        try:
            # 获取原始响应
            print("原始输入:",prompt)
            self.raw_response = self.llm.invoke(prompt).content
            print("原始模型响应:",self.raw_response)  # 调试日志

            # 提取第一个JSON块
            json_str = self.raw_response[self.raw_response.find('{'):self.raw_response.rfind('}') + 1]
            print("第一个json块",json.loads(json_str, strict=False))

            # 严格解析
            return json.loads(json_str, strict=False)

        except json.JSONDecodeError as e:
            raise ValueError(f"JSON解析失败，原始响应：{self.raw_response}") from e
        except Exception as e:
            raise RuntimeError(f"意图解析异常：{str(e)}") from e


    #生成最终可读的结果
    def _format_response(self, data: dict,question:str) -> str:
        print("这是一个测试位置：",self.raw_response)
        explanation_prompt = f"""
        请根据以下查询结果，用简洁明了的一句话中文回答问题：

        原始问题：{question}
        查询结果：{data}
        
        注意：
        1.如果{self.raw_response}的intent字段是”free_room“，那么该查询结果完整展示json中data字段中的room列
        2.如果{self.raw_response}的intent字段是”get_purchase“，那么该查询结果完整逐条列出json中的data字段中的place、time、type、amount列
        3.如果是推荐、总结类具有强逻辑推理需要的原始问题，给出回答时尽量同时给出详细的理由
        """
        return self.llm.invoke(explanation_prompt).content

    #复杂推理任务的处理
    def _handle_logistical_request(self, studentId: str, question: str) -> str:
        try:
            # 步骤1：并行调用多个接口
            self.aggregated_data = {}
            for service in self.logistical_services:
                service_config = self.service_endpoints.get(service)
                if not service_config:
                    return "暂不支持该类型的查询服务"
                params = {
                    **service_config["params"],
                    "studentId": studentId
                }
                response = requests.request(
                    method=service_config["method"],
                    url=service_config["url"],
                    data=params,
                    headers={'Content-Type': 'application/x-www-form-urlencoded'},
                    timeout=10
                )
                self.aggregated_data[self.service_endpoints[service]["description"]] = response.json()

            # 步骤3：生成推理结果
            print("综合推理的数据：",self.aggregated_data)
            return self._format_response(self.aggregated_data, question)
        except Exception as e:
            return f"智能推荐服务暂时不可用：{str(e)}"


    # 强化版generate_answer方法 生成最终的答案
    def generate_answer(self, question: str, studentId: str) -> str:
        try:
            # 步骤1：解析用户意图
            intent_info = self.parse_intent(question)
            service_type = intent_info["intent"]
            if service_type == "logistical":
                return self._handle_logistical_request(studentId, question)

            # 步骤2：获取服务配置
            service_config = self.service_endpoints.get(service_type)
            if not service_config:
                return "暂不支持该类型的查询服务"

            # 步骤3：准备参数
            params = {
                **service_config["params"],
                **intent_info.get("parameters", {}),
                "studentId": studentId
            }

            print(f"[DEBUG] 请求参数：{params}")
            print(f"[DEBUG] 目标URL：{service_config['url']}")

            # 步骤4：发送请求
            response = requests.request(
                method=service_config["method"],
                url=service_config["url"],
                data=params,
                headers={'Content-Type': 'application/x-www-form-urlencoded'},
                timeout=10
            )


            print(f"[DEBUG] 响应状态码：{response.status_code}")
            print(f"[DEBUG] 原始响应内容：{response.text}")

            if response.status_code != 200:
                return f"校园服务暂时不可用（错误码：{response.status_code}）"

            response_data = response.json()
            return self._format_response(response_data, question)

        except requests.exceptions.Timeout:
            return "校园服务响应超时，请稍后重试"
        except requests.exceptions.ConnectionError:
            return "无法连接校园服务，请检查网络"
        except json.JSONDecodeError:
            return "校园服务返回数据格式异常"
        except KeyError as e:
            return f"服务配置缺失关键项：{str(e)}"
        except Exception as e:
            return f"系统处理异常：{str(e)}"



